package com.jeasonzhao.commons.xml;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.ResourceException;
import com.jeasonzhao.commons.utils.ResourceHelper;

public class XMLHelper
{
    public final static String Default_Encoding = "utf-8";
    private static final IXMLBridge m_bridge = new Dom4JXmlBridge();
    private XMLHelper()
    {
    }

    public static XMLNode fromString(String strXml)
        throws XMLException
    {
        if(Algorithms.isEmpty(strXml))
        {
            return null;
        }
        else if(hasUniqueRootNode(strXml) == false)
        {
            strXml = "<Generated__Root__Node>" + strXml + "</Generated__Root__Node>";
        }
        return m_bridge.read(strXml);
    }

    //not safe
    //@todo maybe good way to do it.
    public static boolean hasUniqueRootNode(String strXml)
    {
        if(Algorithms.isEmpty(strXml))
        {
            return true; //empty node,null node
        }
        int ndeep = 0;
        int npos = 0;
        int nrootnodes = 0;
        while(npos < strXml.length())
        {
            char c = strXml.charAt(npos);
            npos++;
            if(c != '<' || npos >= strXml.length())
            {
                continue;
            }
            c = strXml.charAt(npos);
            String name = "";
            if(c == '!')
            {
                //remove comments/cdata
                while(npos < strXml.length())
                {
                    c = strXml.charAt(npos);
                    npos++;
                    if(c == '>')
                    {
                        break;
                    }
                }
            }
            else
            {
                while(npos < strXml.length())
                {
                    c = strXml.charAt(npos);
                    npos++;
                    if(c == '>')
                    {
                        break;
                    }
                    else
                    {
                        name += c;
                    }
                }
                name = name.trim();
                if(name.endsWith("/"))
                {
                    if(ndeep == 0)
                    {
                        nrootnodes++;
                    }
                    continue;
                }
                else if(name.startsWith("/"))
                {
                    ndeep--;
                }
                else
                {
                    ndeep++;
                    if(ndeep == 1)
                    {
                        nrootnodes++;
                    }
                }
//                System.out.println(name + "     " + ndeep);
            }
        }
        return ndeep == 0 && nrootnodes == 1;
    }

    public static XMLNode fromResource(String strFileName)
        throws XMLException,ResourceException
    {
        return fromResource(null,strFileName);
    }

    public static XMLNode fromResource(Class<?> cls,String strFileName)
        throws XMLException,ResourceException
    {
        java.io.InputStream stream = ResourceHelper.streamFromResource(cls,strFileName);
        try
        {
            return m_bridge.read(stream);
        }
        catch(Exception ex)
        {
            throw new XMLException(ex);
        }
        finally
        {
            if(null != stream)
            {
                try
                {
                    stream.close();
                }
                catch(IOException ex1)
                {
                	ex1.printStackTrace();
                }
            }
        }
    }

    public static XMLNode myConfiguration(Class<?> cls,String strFileNameSuffix)
        throws XMLException,ResourceException
    {
        if(null == strFileNameSuffix || strFileNameSuffix.trim().length() < 1)
        {
            strFileNameSuffix = "";
        }
        strFileNameSuffix = strFileNameSuffix.trim();
        return null == cls ? null : fromClassPath(cls,cls.getSimpleName() + strFileNameSuffix);
    }

    public static XMLNode myConfiguration(Class<?> cls)
        throws XMLException,ResourceException
    {
        return myConfiguration(cls,".xml");
    }

    public static XMLNode fromClassPath(Class<?> cls,String strFileName)
        throws XMLException,ResourceException
    {
        java.io.InputStream stream = ResourceHelper.streamFromClassPath(cls,strFileName);
        try
        {
            return m_bridge.read(stream);
        }
        catch(Exception ex)
        {
            throw new XMLException(ex);
        }
        finally
        {
            if(null != stream)
            {
                try
                {
                    stream.close();
                }
                catch(IOException ex1)
                {ex1.printStackTrace();
                }
            }
        }
    }

    public static String getXmlString(XMLNode node)
        throws XMLException
    {
        return getXmlString(node,null,false,false);
    }

    public static String getXmlString(XMLNode node
                                      ,String strEncoding
                                      ,boolean showWholeXMLDocuments
                                      ,boolean isCompatMode)
        throws XMLException
    {
        if(Algorithms.isEmpty(strEncoding))
        {
            strEncoding = Default_Encoding;
        }
        java.io.ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream();
        m_bridge.write(node,buf,strEncoding,showWholeXMLDocuments,isCompatMode);
        try
        {

            String str = new String(buf.toByteArray(),strEncoding);
            return str;
        }
        catch(UnsupportedEncodingException ex)
        {
            throw new XMLException(ex.getMessage(),ex);
        }
        finally
        {
            if(null != buf)
            {
                try
                {
                    buf.close();
                }
                catch(IOException ex1)
                {
                	ex1.printStackTrace();
                }
            }

        }
    }

    public static void writeXML(XMLNode node,java.io.OutputStream stream)
        throws XMLException
    {
        writeXML(node,stream,null,true,false);
    }

    public static void writeXML(XMLNode node
                                ,java.io.OutputStream stream
                                ,String strEncoding
                                ,boolean showWholeXMLDocuments
                                ,boolean isCompatMode
        )
        throws XMLException
    {
        if(Algorithms.isEmpty(strEncoding))
        {
            strEncoding = Default_Encoding;
        }
        m_bridge.write(node,stream,strEncoding,showWholeXMLDocuments,isCompatMode);
    }

//    public static void main(String[] argvs)
//        throws Exception
//    {
//        System.out.println(hasUniqueRootNode("<a/>"));
//    }
}
